package com.jwdemo
{
	
	import com.adobe.utils.*;
	
	import flash.display.*;
	import flash.display3D.*;
	import flash.display3D.textures.*;
	import flash.events.*;
	import flash.geom.Matrix;
	import flash.geom.Matrix3D;
	import flash.geom.Point;
	import flash.geom.Vector3D;
	import flash.utils.Dictionary;
	

	public class QuadBatch
	{
		private var context3D:Context3D ;	

		private var vindex:int 			= 	0 ;
		private var vertexdata:Vector.<Number> 	= 	new Vector.<Number>() ;	// Vertex data
		private var uvdata:Vector.<Number> 	= 	new Vector.<Number>() ;	// UV data
		private var indexdata:Vector.<uint>	=	new Vector.<uint>() ;

		
		private var sheet:SpriteSheet	 ;		// SpriteSheet - mainly used to retrieve UVs
		private var tilesetTexture:Texture ;	// Texture from SpriteSheet

		private var cw:Number ;					// Half Cell Width, Cell Height
		private var ch:Number ;
		private var p1:Vector3D ;				// local coords of each cell.				
		private var p2:Vector3D ;
		private var p3:Vector3D ;
		private var p4:Vector3D ;
		
		private var _oMatrix:Matrix3D 	;		// Othog Translations - maps screen  from (-1,-1) to (1,1)
		private var identMatrix:Matrix3D 	;	// identity matrix.

		private var z:Number					=	0 ;

		// Stage3D stuff
		private var shaderProgram:Program3D ;	
		private var vertexBuffer:VertexBuffer3D ;	
		private var uvBuffer : VertexBuffer3D;
		private var indexBuffer:IndexBuffer3D ;		// the uploaded indices of each vertex of the mesh
		private var _pMatrix:Matrix3D 			= new Matrix3D() ;
		private var lastDrawCount:uint 			= 	0 ;
		private var shader : Program3D;

		public function QuadBatch(ctx:Context3D,sheet:SpriteSheet,oMatrix:Matrix3D)
		{
			this.context3D	=	ctx ;
			this.sheet = sheet ;
			
			var tilesetTextureData:BitmapData = sheet.getImage();
			
			tilesetTexture	= context3D.createTexture(sheet.getSheetWidth(), sheet.getSheetHeight(), 
				Context3DTextureFormat.BGRA, false) ;
			
			uploadTexture(tilesetTexture, tilesetTextureData,false) ;
			
			cw		=	sheet.getFrameWidth()*0.5 ;
			ch		=	sheet.getFrameHeight()*0.5 ;
			
			p1		=	new Vector3D(-cw, ch,0,0) ;
			p2		=	new Vector3D(-cw, -ch,0,0) ;
			p3		=	new Vector3D(cw, -ch,0,0) ;
			p4		=	new Vector3D(cw, ch,0,0) ;
			
			_oMatrix	=	oMatrix ;
			
			identMatrix	=	new Matrix3D() ;
			identMatrix.identity() ;
			
			z			=	0 ;

			setupShaders();

			resetVertexData() ;
			
		}
		
		public function drawSheet(x:Number, y:Number, 
								  frame:int = 0, 
								  rot:Number = 0.0, sx:Number = 1, sy:Number = 1, zD:Number = 0) : void
		{
			
			var uvData:Vector.<Number>	=	sheet.getUVVectorData(frame) ;

			// Do this in AGAL?   Pass _oMatrix,sx,sy,x,y and rot
			
			_pMatrix.identity() ;
			_pMatrix.appendScale(sx,sy,1) ;
			_pMatrix.appendRotation(rot,new Vector3D(0,0,1));
			_pMatrix.appendTranslation(x,y,0) ;
			_pMatrix.append(_oMatrix) ;
			
			// Can we form this in AGAL?
			// Sx,Sy are scaling factor
			// cos = cos(rotation)
			// sin = sin(rotation)
			// tx,ty are wanted x,y position for sprite
			// x and y are between -tilesize/2 to +tilesize/2
			// z is rendering order 0 <= z < 1 	
			/*
			| 2/sw   0     0   -1|			| Sx*cos    -Sy*sin  0   tx|     | x |
			|  0    -2/sh  0    1|   x   	| Sx*sin     Sy*cos  0   ty|  x  | y |
			|  0     0     1    0|			| 0          0       1   0 |	 | z |
			|  0     0     0    1| 			| 0          0       0   1 |     | 1 |
			*/

			var tp1:Vector3D = _pMatrix.transformVector(p1) ;
			var tp2:Vector3D = _pMatrix.transformVector(p2) ;
			var tp3:Vector3D = _pMatrix.transformVector(p3) ;
			var tp4:Vector3D = _pMatrix.transformVector(p4) ;
			
			vertexdata.push(
				tp1.x, tp1.y, zD,	// bottom left
				tp2.x, tp2.y, zD,	// top left
				tp3.x, tp3.y, zD,	// top right
				tp4.x, tp4.y, zD		// bottom right
			) ;
			// UVs ordered as above.			
			uvdata.push(uvData[0], uvData[1],
						uvData[2], uvData[3],	
						uvData[4], uvData[5],
						uvData[6], uvData[7] ) ;
			
			
			indexdata.push(
				0+vindex, 1+vindex, 2+vindex, 		
				0+vindex, 2+vindex, 3+vindex
			) ;
			
			vindex += 4 ;

			
		}
		
		public function numberTriangles():uint 
		{
			return  lastDrawCount;
		}
		
		
		private function setupShaders() : void
		{
			var vertexShaderAssembler:AGALMiniAssembler = new AGALMiniAssembler();
			vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
				"m44 op, va0, vc0\n" +
				// tell fragment shader about UV
				"mov v1, va1\n"

			);
			
			var fragmentShaderAssembler:AGALMiniAssembler = new AGALMiniAssembler();
			fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
				// grab the texture color from texture fs0
				// using the UV coordinates stored in v1
				//				"tex ft0, v1, fs0 <2d,repeat,miplinear>\n" +	
				"tex ft0, v1, fs0 <2d,linear, nomip>\n" +	
				"mov oc,ft0\n"
			);
			
			shader = context3D.createProgram();
			shader.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode );
		}
		
		
		// Alternative - resets Program and BlendFactors.
		
		public function draw():void
		{
			context3D.setProgram(shader);
			context3D.setBlendFactors(Context3DBlendFactor.ONE, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA);            

			// Just use an Identity Matrix .. for now..
			context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, identMatrix, true); 
			context3D.setTextureAt(0, tilesetTexture);
			
			vertexBuffer = context3D.createVertexBuffer(vertexdata.length / 3, 3); 
			vertexBuffer.uploadFromVector(vertexdata, 0, vertexdata.length / 3);

			uvBuffer = context3D.createVertexBuffer(uvdata.length/2, 2);
			uvBuffer.uploadFromVector(uvdata, 0, uvdata.length/2);
				
			indexBuffer = context3D.createIndexBuffer(indexdata.length);
			indexBuffer.uploadFromVector(indexdata, 0, indexdata.length);

						
			context3D.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
			context3D.setVertexBufferAt(1, uvBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);

			context3D.drawTriangles(indexBuffer, 0,  indexdata.length/3);
	
			lastDrawCount	=	indexdata.length/3

			resetVertexData() ;


		}
		
		private function resetVertexData() : void
		{
			// Reset Index for next draw
			vindex 		=	0 ;
			uvdata		= 	new Vector.<Number>()  ;
			vertexdata	= 	new Vector.<Number>()  ;
			indexdata	=	new Vector.<uint>() ;
		}
		

		private function uploadTexture(dest:Texture, src:BitmapData, withMipMaps:Boolean = false):void
		{
			var ws:int = src.width;
			var hs:int = src.height;
			var level:int = 0;
			var tmp:BitmapData;
			var transform:Matrix = new Matrix();
			
			tmp = new BitmapData(src.width, src.height, true, 0x00000000);
			
			while ( ws >= 1 && hs >= 1 )
			{ 
				tmp.draw(src, transform, null, null, null, true); 
				dest.uploadFromBitmapData(tmp, level);
				if (!withMipMaps)
				{
					break ;
				}
				
				transform.scale(0.5, 0.5);
				
				level++;
				ws >>= 1;
				hs >>= 1;
				if (hs && ws) 
				{
					tmp.dispose();
					tmp = new BitmapData(ws, hs, true, 0x00000000);
				}
			}
			tmp.dispose();
		}
	}

	

}